﻿using System;
using System.Globalization;
using System.Text;

namespace Space2D_Utils
{
	public class Int128Fraction
	{
		public Int128Fraction(Int128 divident)
		{
			Divident = divident;
			Divider = 1;
		}

		public Int128Fraction(Int128 divident, Int128 divider)
		{
			Int128 nod = MathUtils.NOD(divident, divider);

			if (divider.Sign < 0)
			{
				divider = -divider;
				divident = -divident;
			}

			if (nod > 1)
			{
				divident /= nod;
				divider /= nod;
			}

			Divident = divident;
			Divider = divider;
		}

		public double ToDouble()
		{
			return (double)Divident / (double)Divider;
		}

		public override int GetHashCode()
		{
			return Divident.GetHashCode() ^ Divider.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			return base.Equals(obj);
		}

		public bool Equals(Int128Fraction obj)
		{
			return Divident == obj.Divident && Divider == obj.Divider;
		}

		public override string ToString()
		{
			return Divident + "/" + Divider;
		}

		public static Int128Fraction FromDouble(double dValue)
		{
			if (dValue % 1 == 0)	// if whole number
			{
				return new Int128Fraction((Int128)dValue);
			}
			else
			{
				double dTemp = dValue;
				long iMultiple = 1;
				string strTemp = dValue.ToString();
				while (strTemp.IndexOf("E") > 0)	// if in the form like 12E-9
				{
					dTemp *= 10;
					iMultiple *= 10;
					strTemp = dTemp.ToString();
				}
				int i = 0;
				while (strTemp[i] != '.')
					i++;
				int iDigitsAfterDecimal = strTemp.Length - i - 1;
				while (iDigitsAfterDecimal > 0)
				{
					dTemp *= 10;
					iMultiple *= 10;
					iDigitsAfterDecimal--;
				}
				return new Int128Fraction((int)Math.Round(dTemp), iMultiple);
			}
		}

		public static Int128Fraction Parse(string val)
		{
			string[] values = val.Split('/');

			if (values.Length == 1)
			{
				return FromDouble(double.Parse(val));
			}
			else
			{
				Int128 divident = Int128.Parse(values[0]);
				Int128 divider = Int128.Parse(values[1]);

				return new Int128Fraction(divident, divider);
			}
		}

		public static bool TryParse(string val, out Int128Fraction result)
		{
			result = Zero;

			string[] values = val.Split('/');

			Int128 divident = 0;
			Int128 divider = 1;

			if (values.Length == 1)
			{
				double res = 0;

				if (double.TryParse(val, out res))
				{
					result = FromDouble(res);

					return true;
				}
			}
			else if (values.Length == 2 && Int128.TryParse(values[0], out divident) && Int128.TryParse(values[1], out divider))
			{
				result = new Int128Fraction(divident, divider);

				return true;
			}

			return false;
		}

		public static implicit operator Int128Fraction(bool value)
		{
			return new Int128Fraction(value);
		}

		public static implicit operator Int128Fraction(byte value)
		{
			return new Int128Fraction(value);
		}

		public static implicit operator Int128Fraction(char value)
		{
			return new Int128Fraction(value);
		}

		public static explicit operator Int128Fraction(decimal value)
		{
			return new Int128Fraction(new Int128(value));
		}

		public static explicit operator Int128Fraction(double value)
		{
			return new Int128Fraction(new Int128(value));
		}

		public static implicit operator Int128Fraction(short value)
		{
			return new Int128Fraction(value);
		}

		public static implicit operator Int128Fraction(int value)
		{
			return new Int128Fraction(value);
		}

		public static implicit operator Int128Fraction(long value)
		{
			return new Int128Fraction(value);
		}

		public static implicit operator Int128Fraction(sbyte value)
		{
			return new Int128Fraction(value);
		}

		public static explicit operator Int128Fraction(float value)
		{
			return new Int128Fraction(new Int128(value));
		}

		public static implicit operator Int128Fraction(ushort value)
		{
			return new Int128Fraction(value);
		}

		public static implicit operator Int128Fraction(uint value)
		{
			return new Int128Fraction(value);
		}

		public static implicit operator Int128Fraction(ulong value)
		{
			return new Int128Fraction(value);
		}

		public static int Compare(Int128Fraction left, Int128Fraction right)
		{
			if (left.Divider == right.Divider)
			{
				return Int128.Compare(left.Divident, right.Divident);
			}
			else
			{
				return Int128.Compare(left.Divident * right.Divider, right.Divident * left.Divider);
			}
		}

		public static bool operator >(Int128Fraction left, Int128Fraction right)
		{
			return Compare(left, right) > 0;
		}

		public static bool operator <(Int128Fraction left, Int128Fraction right)
		{
			return Compare(left, right) < 0;
		}

		public static bool operator >=(Int128Fraction left, Int128Fraction right)
		{
			return Compare(left, right) >= 0;
		}

		public static bool operator <=(Int128Fraction left, Int128Fraction right)
		{
			return Compare(left, right) <= 0;
		}

		public static bool operator !=(Int128Fraction left, Int128Fraction right)
		{
			return Compare(left, right) != 0;
		}

		public static bool operator ==(Int128Fraction left, Int128Fraction right)
		{
			return Compare(left, right) == 0;
		}

		public static Int128Fraction operator +(Int128Fraction value)
		{
			return value;
		}

		public static Int128Fraction operator -(Int128Fraction value)
		{
			return Negate(value);
		}

		public static Int128Fraction Negate(Int128Fraction value)
		{
			return new Int128Fraction(Int128.Negate(value.Divident), value.Divider);
		}

		public Int128Fraction ToAbs()
		{
			return Abs(this);
		}

		public static Int128Fraction Abs(Int128Fraction value)
		{
			if (value.Divident.Sign < 0)
				return -value;

			return value;
		}

		public static Int128Fraction operator +(Int128Fraction left, Int128Fraction right)
		{
			return new Int128Fraction(left.Divident * right.Divider + right.Divident * left.Divider, left.Divider * right.Divider);
		}

		public static Int128Fraction operator -(Int128Fraction left, Int128Fraction right)
		{
			return left + -right;
		}

		public static Int128Fraction Add(Int128Fraction left, Int128Fraction right)
		{
			return left + right;
		}

		public static Int128Fraction Subtract(Int128Fraction left, Int128Fraction right)
		{
			return left - right;
		}

		public static Int128Fraction Invert(Int128Fraction value)
		{
			return new Int128Fraction(value.Divider, value.Divident);
		}

		public static Int128Fraction Multiply(Int128Fraction left, Int128Fraction right)
		{
			return new Int128Fraction(left.Divident * right.Divident, left.Divider * right.Divider);
		}

		public static Int128Fraction operator *(Int128Fraction left, Int128Fraction right)
		{
			return Multiply(left, right);
		}

		public static Int128Fraction Divide(Int128Fraction dividend, Int128Fraction divisor)
		{
			return Multiply(dividend, Int128Fraction.Invert(divisor));
		}

		public static Int128Fraction operator /(Int128Fraction dividend, Int128Fraction divisor)
		{
			return Divide(dividend, divisor);
		}

		private static Int128Fraction GetZero()
		{
			return new Int128Fraction(0, 1);
		}

		private static Int128Fraction GetOne()
		{
			return new Int128Fraction(1, 1);
		}

		public static Int128Fraction Zero = GetZero();
		public static Int128Fraction One = GetOne();

		public Int128 Divident { get; private set; }
		public Int128 Divider { get; private set; }
	}
}
